package network.io;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.UnknownHostException;

import Signals.AbstractMessage;
import Signals.Goodbye;
import Signals.Hello;
import Signals.HelloAck;
import Signals.TextMessage;
import network.NetworkInterface;

public class UDPReceiver implements Runnable {

	private static final int BUFFER_LENGTH = 1024;

	private NetworkInterface networkInterface;

	/**
	 * Attribute determining if the UDP receiver still needs to receive messages.
	 */
	private volatile boolean connected;

	public UDPReceiver(NetworkInterface networkInterface) throws IOException {
		this.setNetworkInterface(networkInterface);
		this.setConnected(true);
	}

	/** 
	 * We will probably have to replace this infinite loop by an interruption system
	 * 
	 */
	@Override
	public void run() {
		do {
			try {
				this.processMessage(this.receive());
			} catch (IOException e) {
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
		} while (this.isConnected() && !this.networkInterface.getSocket().isClosed());
		this.networkInterface.stopReceiving();
	}

	/**
	 * Retrieves the last received message
	 * @return
	 * @throws IOException
	 * @throws ClassNotFoundException
	 */
	public AbstractMessage receive() throws IOException, ClassNotFoundException   {
		byte[] buf = new byte[BUFFER_LENGTH];
		DatagramPacket packet = new DatagramPacket(buf, buf.length);
		try {
			this.networkInterface.getSocket().receive(packet);
		} catch (IOException e) {
		}
		ByteArrayInputStream in = new ByteArrayInputStream(buf);
		ObjectInputStream objIn = new ObjectInputStream(in);

		// A REVOIR : return (AbstractMessage) objIn.readObject();
		AbstractMessage abs = null;
		abs = (AbstractMessage)objIn.readObject();
		return abs;
	}

	/**
	 * Dispatches the process of a message
	 * @param message
	 */
	public void processMessage(AbstractMessage message) {
		switch (message.getTypeContenu()) {
		case HELLO: this.processHello((Hello) message); break;
		case HELLOACK: this.processHelloAck((HelloAck) message); break;
		case GOODBYE: this.processGoodbye((Goodbye) message); break;
		case TEXTMESSAGE: this.processTextMessage((TextMessage) message); break;
		case FILEMESSAGE: System.out.println("TO DO : FILEMESSAGE PROCESS"); break;
		}
	}

	/**
	 * Processes a Hello message. Adds the Hello sender to the list of connected users
	 * and sends a Hello message back to the Hello sender.
	 * @param helloMessage
	 */
	private void processHello(Hello helloMessage) {
		System.out.println("Recu HELLO de " + helloMessage.getNickname());
		try {
			if(!helloMessage.getNickname().equals(this.getNetworkInterface().getNickname())) {
				this.networkInterface.getMainTabs().getHomePanel().addConnectedUser(helloMessage.getNickname());
				this.networkInterface.getSendManager().sendUnicast(new HelloAck(networkInterface.getNickname()), InetAddress.getByName(networkInterface.parseIP(helloMessage.getNickname())));
			}

		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * this.networkInterface.addUser(helloAckMessage.getNickname() possible duplication with processHello?
	 * @param helloAckMessage
	 */
	private void processHelloAck(HelloAck helloAckMessage) {
		System.out.println("Recu HELLOACK de " + helloAckMessage.getNickname());
		if(!helloAckMessage.getNickname().equals(this.getNetworkInterface().getNickname()))
			this.networkInterface.getMainTabs().getHomePanel().addConnectedUser(helloAckMessage.getNickname());
	}

	/**
	 * Processes a Goodbye message from an other user. Removes the Goodbye sender from the connected users list.
	 * @param goodbyeMessage
	 */
	private void processGoodbye(Goodbye goodbyeMessage) {
		System.out.println("Recu GOODBYE de " + goodbyeMessage.getNickname());
		if(!goodbyeMessage.getNickname().equals(this.getNetworkInterface().getNickname())) {
			this.networkInterface.getMainTabs().getHomePanel().removeConnectedUser(goodbyeMessage.getNickname());
			if(this.networkInterface.getMainTabs().conversationExists(goodbyeMessage.getNickname()))
				this.networkInterface.getMainTabs().displayNewMessageReceived(goodbyeMessage.getNickname(), new TextMessage(goodbyeMessage.getNickname(), "[DISCONNECTED]"));
				
	} else {
			this.networkInterface.closeSocket();
		}
	}

	/**
	 * this.networkInterface.addUser(helloAckMessage.getNickname() possible duplication with processHello?
	 * @param helloAckMessage
	 */
	private void processTextMessage(TextMessage textMessage) {
		System.out.println("Recu TEXTMESSAGE de " + textMessage.getNickname() + " ==> " + textMessage.getMessage());
		if(!textMessage.getNickname().equals(this.getNetworkInterface().getNickname())) {
			this.networkInterface.addUser(textMessage.getNickname());
			this.networkInterface.getMainTabs().displayNewMessageReceived(textMessage.getNickname(), textMessage);
		}
		
	}


	public NetworkInterface getNetworkInterface() {
		return networkInterface;
	}

	public void setNetworkInterface(NetworkInterface networkInterface) {
		this.networkInterface = networkInterface;
	}

	public boolean isConnected() {
		return connected;
	}

	public void setConnected(boolean connected) {
		this.connected = connected;
	}
}

